深入对比Proxy与Object.defineProperty() 您所在的位置:网站首页 js proxy的用法 深入对比Proxy与Object.defineProperty()

深入对比Proxy与Object.defineProperty()

2023-11-16 00:22| 来源: 网络整理| 查看: 265

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。

提问 Object.defineProperty()和proxy的区别? 为什么vue3要选用proxy,好处是什么? proxy

Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。

Proxy的用法,这个大家都知道

const p = new Proxy(target, handler)

剖析一下内部实现 ECMAScript 2017 (ECMA-262) Proxy

image.png

可以看到接收两个参数(target,handler)

如果target是undefined,报错 运行ProxyCreate(target, handler)

下面是ProxyCreate的实现

image.png

排除一下错误处理,核心代码从5开始 先创建一个新的空对象p,

设置p对象的内部方法(除了[[call]]和[[Construct]])设置为9.5指定的定义,

然后设置p的call和Construct方法,

再设置内部属性[[ProxyTarget]]和[[ProxyHandler]]

返回对象p

我们可以用它们拦截什么?

对于对象的大多数操作,JavaScript 规范中有一个所谓的“内部方法”,它描述了最底层的工作方式。例如 [[Get]],用于读取属性的内部方法,[[Set]],用于写入属性的内部方法,等等。这些方法仅在规范中使用,我们不能直接通过方法名调用它们。

Proxy 捕捉器会拦截这些方法的调用。它们在 proxy 规范 和下表中被列出。

对于每个内部方法,此表中都有一个捕捉器:可用于添加到 new Proxy 的 handler 参数中以拦截操作的方法名称:

对于对象的大多数操作,JavaScript 规范中有一个所谓的“内部方法”,它描述了最底层的工作方式。例如 [[Get]],用于读取属性的内部方法,[[Set]],用于写入属性的内部方法,等等。这些方法仅在规范中使用,我们不能直接通过方法名调用它们。

Proxy 捕捉器会拦截这些方法的调用。它们在 proxy 规范 和下表中被列出。

对于每个内部方法,此表中都有一个捕捉器:可用于添加到 new Proxy 的 handler 参数中以拦截操作的方法名称:

内部方法Handler 方法何时触发[[Get]]get读取属性[[Set]]set写入属性[[HasProperty]]hasin 操作符[[Delete]]deletePropertydelete 操作符[[Call]]apply函数调用[[Construct]]constructnew 操作符[[GetPrototypeOf]]getPrototypeOfObject.getPrototypeOf[[SetPrototypeOf]]setPrototypeOfObject.setPrototypeOf[[IsExtensible]]isExtensibleObject.isExtensible[[PreventExtensions]]preventExtensionsObject.preventExtensions[[DefineOwnProperty]]definePropertyObject.defineProperty, Object.defineProperties[[GetOwnProperty]]getOwnPropertyDescriptorObject.getOwnPropertyDescriptor, for..in, Object.keys/values/entries[[OwnPropertyKeys]]ownKeysObject.getOwnPropertyNames, Object.getOwnPropertySymbols, for..in, Object.keys/values/entries Reflect

Reflect 是一个内建对象,可简化 Proxy 的创建。

前面所讲过的内部方法,例如 [[Get]] 和 [[Set]] 等,都只是规范性的,不能直接调用。

Reflect 对象使调用这些内部方法成为了可能。它的方法是内部方法的最小包装。

尤其是,Reflect 允许我们将操作符(new,delete,……)作为函数(Reflect.construct,Reflect.deleteProperty,……)执行调用。这是一个有趣的功能,但是这里还有一点很重要。

对于每个可被 Proxy 捕获的内部方法,在 Reflect 中都有一个对应的方法,其名称和参数与 Proxy 捕捉器相同。

所以,我们可以使用 Reflect 来将操作转发给原始对象。

我们可以把捕捉器重写得更短:

get(target, prop, receiver) { return Reflect.get( ... arguments); }

Reflect 调用的命名与捕捉器的命名完全相同,并且接受相同的参数。它们是以这种方式专门设计的。

因此,return Reflect... 提供了一个安全的方式,可以轻松地转发操作,并确保我们不会忘记与此相关的任何内容。

proxy 的局限性 1. 无法代理内部插槽

许多内建对象,例如 Map,Set,Date,Promise 等,都使用了所谓的“内部插槽”。

例如:

let map = new Map(); let proxy = new Proxy(map, {}); proxy.set('test', 1); // Error

解决方法 在get的时候将get要返回的值先绑定目标对象后返回

let map = new Map(); let proxy = new Proxy(map, { get(target, prop, receiver) { let value = Reflect.get(...arguments); return typeof value == 'function' ? value.bind(target) : value; } }); proxy.set('test', 1); alert(proxy.get('test')); // 1(工作了!) 2. 私有字段也和上面一样 3. peoxy != target

这个很好理解 ,代理对象和目标对象是不=== 的

总结

Proxy 是对象的包装器,将代理上的操作转发到对象,并可以选择捕获其中一些操作。

它可以包装任何类型的对象,包括类和函数。

语法为:

let proxy = new Proxy(target, { /* trap */ });

……然后,我们应该在所有地方使用 proxy 而不是 target。代理没有自己的属性或方法。如果提供了捕捉器(trap),它将捕获操作,否则会将其转发给 target 对象。

我们可以捕获

get,set,deleteProperty 等操作 函数调用(apply捕捉器) new操作(construct 捕捉器)

Reflect 旨在补充 Proxy。对于任意 Proxy 捕捉器,都有一个带有相同参数的 Reflect 调用。我们应该使用它们将调用转发给目标对象。

Proxy的局限

无法代理内部对象的内部插槽 无法代理私有字段 代理对象和目标对象不相等 Object.defineProperty

Object.defineProperty()  方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

语法 Object.defineProperties(obj, props) 描述

对象里目前存在的属性描述符有两种主要形式:数据(属性)描述符和存取描述符(访问器属性)。数据描述符是一个具有值的属性,该值可以是可写的,也可以是不可写的。存取描述符是由 getter 函数和 setter 函数所描述的属性。一个描述符只能是这两者其中之一;不能同时是两者。

属性描述符 value — 值 writable — 如果为 true,则值可以被修改,否则它是只可读的。 enumerable — 如果为 true,则会被在循环中列出,否则不会被列出。 configurable — 如果为 true,则此属性可以被删除,这些特性也可以被修改,否则不可以。 访问器属性 get —— 一个没有参数的函数,在读取属性时工作, set —— 带有一个参数的函数,当属性被设置时调用, enumerable —— 与数据属性的相同, configurable —— 与数据属性的相同。 回答第一个问题

Object.defineProperty()和proxy的区别?

Object.definePropertyProxy新增/修改一个对象的属性,定义其描述,返回该对象代理目标对象,对其操作拦截,返回代理对象有数据描述符和访问器描述符两种对其13种操作进行拦截只能代理常规对象可以代理任何对象(函数,数组,类)-不能代理内部对象的内部插槽 回答第二个问题

为什么vue3要选用proxy,好处是什么?

能够代理任何对象包括数组和函数,对象 比Object.defineProperty()更多的基本语义得操作(get,set,delete...) 不用循环遍历对象然后再使用Object.defineProperty(),Proxy可以代理对象内所有的属性。 Object.defineProperty()只能劫持对象的属性(给对象添加属性vue无法检测到)


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有